package ray;

import ray.math.Color;
import ray.math.Point3;
import ray.math.Ray;
import ray.math.Vector3;

/**
 * A simple ray tracer.
 *
 * @author ags
 */
public class RayTracer {

	/**
	 * The main method takes all the parameters and assumes they are input files
	 * for the ray tracer. It tries to render each one and write it out to a PNG
	 * file named <input_file>.png.
	 *
	 * @param args
	 */
	static String []defaultArgs = {"scenes/four-spheres.xml",
							"scenes/one-box.xml",
							"scenes/one-sphere.xml",
							"scenes/two-boxes.xml",
							"scenes/wire-box-axon.xml",
							"scenes/wire-box-orth.xml",
							"scenes/wire-box-per.xml",
							"scenes/wire-box-sper.xml",
	};
	public static final void main(String[] args) {
		//args = defaultArgs;
		Parser parser = new Parser();
		for (int ctr = 0; ctr < args.length; ctr++) {
			try {
			// Get the input/output filenames.
			String inputFilename = args[ctr];
			String outputFilename = inputFilename + ".png";

			// Parse the input file
			Scene scene = (Scene) parser.parse(inputFilename, Scene.class);

			// Render the scene
			renderImage(scene);

			// Write the image out
			scene.getImage().write(outputFilename);
			} catch (Exception e) {
				System.out.println("Error rendering image "+ctr);
				e.printStackTrace();
			}
		}
	}

	/**
	 * The renderImage method renders the entire scene.
	 *
	 * @param scene The scene to be rendered
	 */
	public static void renderImage(Scene scene) {
		// Get the output image
		Image image = scene.getImage();

		// Timing counters
		long startTime = System.currentTimeMillis();

		/*
		 * Render the image, writing the pixel values into image.
		 */
		
		OrthonormalBasis oBasis = new OrthonormalBasis();
		oBasis.compute(scene.camera.projNormal, scene.camera.viewUp);
		
		double pixelHeight = scene.camera.viewHeight/image.height;
		double pixelWidth  = scene.camera.viewWidth/image.width;
		
		Point3 projCenter = new Point3(scene.camera.viewPoint.x,scene.camera.viewPoint.y,scene.camera.viewPoint.z);
		
		Vector3 normViewDir = new Vector3(scene.camera.viewDir.x,scene.camera.viewDir.y,scene.camera.viewDir.z);
		normViewDir.normalize();
		
		projCenter.scaleAdd(scene.camera.projDistance, normViewDir);
		
		double projPlaneLeft   = -(image.width *0.5)*pixelWidth;
		double projPlaneBottom = -(image.height*0.5)*pixelHeight;
				
		for(int y = 0;y<image.height;y++){
			for(int x = 0;x<image.width;x++){
				// Note: (x,y) should be (x+.5,y+.5), otherwise the image is shifted half a pixel down and right
				// however doing that no longer matches the example images
				double uScale = projPlaneLeft + pixelWidth/2 + x*pixelWidth;
				double vScale = projPlaneBottom + pixelHeight/2 + y*pixelHeight;
				
				Point3 s = new Point3(projCenter.x,projCenter.y,projCenter.z);
				s.scaleAdd(uScale, oBasis.u);
				s.scaleAdd(vScale, oBasis.v);
				
				Ray ray = new Ray();
				ray.calc(scene.camera.viewPoint, s);

				Color color = scene.calcColor(ray);
				image.setPixelColor(color, x, y);
			}
		}
		
		// Output time
		long totalTime = (System.currentTimeMillis() - startTime);
		System.out.println("Done.  Total rendering time: "
				+ (totalTime / 1000.0) + " seconds");
	}
}
